﻿#include<iostream>
#include <xlnt/xlnt.hpp>
#include<afxwin.h>
#include <wchar.h>
#include <thread>
#include <windows.h>
#include <fstream>
using namespace std;

#define KEY_PRESSED(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x0001) ? 1:0) //如果为真，表示按下过
#define KEY_PRESSING(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)  //如果为真，表示正处于按下状态

struct g_oper {
    int key; //键码值，组合键则用英文逗号隔开
    string keyName;
    std::vector<INT> keys;
    std::vector<string> keyNames;
    int down; //按下延迟
    int up; //弹起延迟
    int dx; //横坐标
    int dy; //纵坐标
    friend std::ostream& operator <<(std::ostream& os, g_oper const& a) {
        return os << '\n'
            << "热键\t= " << a.keyName << "" << '\n'
            << "按下\t= " << a.down << "" << '\n'
            << "弹起\t= " << a.up << "" << '\n'
            << "坐标\t= " << a.dx << "," << a.dy << "" << '\n'
            ;
    }
};

struct g_script {
    int idx;
    string name;
    bool enable;
    int key;
    string keyName;
    std::vector<INT> keys;
    std::vector<string> keyNames;
    int loop;
    friend std::ostream& operator <<(std::ostream& os, g_script const& a) {
        return os << '\n'
            << "名称\t= " << a.name << "" << '\n'
            //<< "启用\t= " << (a.enable ? "是" : "否") << "" << '\n'
            << "热键\t= " << a.keyName << "" << '\n'
            << "循环\t= " << a.loop << "" << '\n'
            ;
    }
};

// 窗口句柄
HWND hwnd = NULL;
// 是否已暂停
bool pause = false;
// 脚本列表
std::vector<g_script> scriptList = {};
std::vector<vector<g_oper>> operAllList = {};
std::map<int, bool> finishMap;

// 延迟
void sleep(int delay) {
    Sleep(delay);
}

void MouseMove(int x, int y)//鼠标移动到指定位置  
{
    double fScreenWidth = ::GetSystemMetrics(SM_CXSCREEN) - 1;//获取屏幕分辨率宽度  
    double fScreenHeight = ::GetSystemMetrics(SM_CYSCREEN) - 1;//获取屏幕分辨率高度  
    double fx = x * (65535.0f / fScreenWidth);
    double fy = y * (65535.0f / fScreenHeight);
    INPUT  Input = { 0 };
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
    Input.mi.dx = fx;
    Input.mi.dy = fy;
    SendInput(1, &Input, sizeof(INPUT));
}

void MouseLeftDown()//鼠标左键按下  
{
    INPUT  Input = { 0 };
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    SendInput(1, &Input, sizeof(INPUT));
}

void MouseLeftUp()//鼠标左键放开  
{
    INPUT  Input = { 0 };
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
    SendInput(1, &Input, sizeof(INPUT));

}

void MouseRightDown()//鼠标右键按下  
{
    INPUT  Input = { 0 };
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
    SendInput(1, &Input, sizeof(INPUT));
}

void MouseRightUp()//鼠标右键放开  
{
    INPUT  Input = { 0 };
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_RIGHTUP;
    SendInput(1, &Input, sizeof(INPUT));
}

// 按下指定位置，指定延迟
void mouseClick(int x, int y, int delay)
{
    while (pause) {
        Sleep(500);
    }
    // 窗口坐标转屏幕坐标
    POINT pos = {x, y};
    POINT res = pos;
    ClientToScreen(hwnd, &res);
    x = res.x, y = res.y;
   // cout << "窗口坐标: (" << pos.x << "," << pos.y << ")，屏幕坐标: (" << res.x << "," << res.y << ")" << endl;

    // 执行按键操作
    INPUT Input = { 0 };
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN;
    SetCursorPos(x, y); // 移动鼠标位置
    SendInput(1, &Input, sizeof(INPUT));

    sleep(delay);

    ZeroMemory(&Input, sizeof(INPUT));
    Input.type = INPUT_MOUSE;
    Input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP;
    SendInput(1, &Input, sizeof(INPUT));
}

// 鼠标按下
void mouseDown(int x, int y)
{
    while (pause) {
        Sleep(500);
    }
    if (hwnd != NULL) {
        // 窗口坐标转屏幕坐标
        POINT pos = { x, y };
        POINT res = pos;
        ClientToScreen(hwnd, &res);
        x = res.x, y = res.y;
        MouseMove(x, y);//鼠标移动到指定位置  
    }
    MouseLeftDown();//鼠标左键点下  
}

// 鼠标弹起
void mouseUp(int x, int y)
{
    if (hwnd != NULL) {
        // 窗口坐标转屏幕坐标
        POINT pos = { x, y };
        POINT res = pos;
        ClientToScreen(hwnd, &res);
        x = res.x, y = res.y;
        MouseMove(x, y);//鼠标移动到指定位置
    }
    Sleep(10);//这里需要等待一下，不然拖动会没有效果
    MouseLeftUp();//鼠标释放  
}

// 按下组合键，指定延迟
void keyPress(std::vector<INT> keys, int delay)
{
    while (pause) {
        Sleep(500);
    }
    int size = keys.size();
    KEYBDINPUT keybdInput[3];
    INPUT input[3];
    for (int i = 0; i < size; i++) {
        keybdInput[i].wVk = keys[i];
        keybdInput[i].dwFlags = 0;
    }
    for (int i = 0; i < size; i++) {
        input[i].type = INPUT_KEYBOARD;
        input[i].ki = keybdInput[i];
    }
    SendInput(2, input, sizeof(INPUT));

    sleep(delay);

    for (int i = 0; i < size; i++) {
        input[i].ki.dwFlags = KEYEVENTF_KEYUP;
    }
    SendInput(2, input, sizeof(INPUT));
}

// 按下按键
void keyDown(std::vector<INT> keys) 
{
    while (pause) {
        Sleep(500);
    }
    int size = keys.size();
    KEYBDINPUT keybdInput[3];
    INPUT input[3];
    for (int i = 0; i < size; i++) {
        keybdInput[i].wVk = keys[i];
        keybdInput[i].dwFlags = 0;
    }
    for (int i = 0; i < size; i++) {
        input[i].type = INPUT_KEYBOARD;
        input[i].ki = keybdInput[i];
    }
    SendInput(size, input, sizeof(INPUT));
}
// 弹起按键
void keyUp(std::vector<INT> keys) 
{
    int size = keys.size();
    KEYBDINPUT keybdInput[3];
    INPUT input[3];
    for (int i = 0; i < size; i++) {
        keybdInput[i].wVk = keys[i];
        keybdInput[i].dwFlags = KEYEVENTF_KEYUP;
    }
    for (int i = 0; i < size; i++) {
        input[i].type = INPUT_KEYBOARD;
        input[i].ki = keybdInput[i];
    }
    SendInput(size, input, sizeof(INPUT));
}

// 普通sting类型 转 UTF-8编码格式字符串
std::string string_To_UTF8(const std::string& str)
{
    int nwLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
    wchar_t* pwBuf = new wchar_t[nwLen + 1];//一定要加1，不然会出现尾巴
    ZeroMemory(pwBuf, nwLen * 2 + 2);
    ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length(), pwBuf, nwLen);
    int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
    char* pBuf = new char[nLen + 1];
    ZeroMemory(pBuf, nLen + 1);
    ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
    std::string retStr(pBuf);
    delete[]pwBuf;
    delete[]pBuf;
    pwBuf = NULL;
    pBuf = NULL;
    return retStr;
}

//UTF-8编码格式字符串 转 普通sting类型
std::string UTF8_To_string(const std::string& str)
{
    int nwLen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
    wchar_t* pwBuf = new wchar_t[nwLen + 1];//一定要加1，不然会出现尾巴
    memset(pwBuf, 0, nwLen * 2 + 2);
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), pwBuf, nwLen);
    int nLen = WideCharToMultiByte(CP_ACP, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
    char* pBuf = new char[nLen + 1];
    memset(pBuf, 0, nLen + 1);
    WideCharToMultiByte(CP_ACP, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
    std::string retStr = pBuf;
    delete[]pBuf;
    delete[]pwBuf;
    pBuf = NULL;
    pwBuf = NULL;
    return retStr;
}

std::vector<std::vector<std::string>> readExcel(string sheetName)
{
    xlnt::workbook wb;
    wb.load(string_To_UTF8("脚本方案.xlsx"));
    auto ws = wb.sheet_by_title(string_To_UTF8(sheetName));
    std::vector<std::vector<std::string>> theWholeSpreadSheet;
    for (auto row : ws.rows(false)) {
        std::vector<std::string> aSingleRow;
        for (auto cell : row) {
            aSingleRow.push_back(cell.to_string());
        }
        theWholeSpreadSheet.push_back(aSingleRow);
    }
    return theWholeSpreadSheet;
}

bool isNumeric(std::string const& str) {
    auto it = str.begin();
    while (it != str.end() && std::isdigit(*it)) {
        it++;
    }
    return !str.empty() && it == str.end();
}

// string转int
int string_To_int(string s) {
    return atoi(s.c_str());
}

// string切割
std::vector<std::string> split(const std::string& str, const std::string& delimiter)
{
    std::vector<std::string> tokens;
    std::string::size_type lastPos = 0;
    std::string::size_type pos = str.find(delimiter, lastPos);
    while (pos != std::string::npos)
    {
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        lastPos = pos + delimiter.size();
        pos = str.find(delimiter, lastPos);
    }
    tokens.push_back(str.substr(lastPos));
    return tokens;
}

// 读取键值
int getKeyVal(string s) {
    if (s.size() > 2 && s.substr(0, 2) == "0x") {
        return std::stoi(s, nullptr, 16);
    }
    if (s.length() == 1) {
        if (isNumeric(s)) {
            if (s.length() == 1) {
                return 0x30 + atoi(s.c_str());
            }
        }
        else {
            return s[0];
        }
    }
    else if (s.compare("leftClick") == 0) return 0x01;
    else if (s.compare("rightClick") == 0) return 0x02;
    else if (s.compare("ctrl") == 0) return 17;
    else if (s.compare("space") == 0) return 32;
    else if (s.compare("tab") == 0) return 9;
    else {
        std::cout << "键码识别错误：" + s;
        exit(0);
        system("pause");
    }
}

// 批量读取键值
std::vector<INT> getKeyValBatch(std::vector<string> ss) {
    std::vector<INT> res = {};
    for (int i = 0; i < ss.size(); i++) {
        int key = getKeyVal(ss[i]);
        res.push_back(key);
    }
    return res;
}

// 加载配置
void loadConfig() {
    std::vector<std::vector<std::string>> mainSheetList = readExcel("主配置");
    int idx = 0;
    for (int rowInt = 1; rowInt < mainSheetList.size(); rowInt++) {
        g_script gs = {};
        gs.idx = idx++;
        gs.name = UTF8_To_string(mainSheetList.at(rowInt).at(0));
        gs.enable = UTF8_To_string(mainSheetList.at(rowInt).at(1)).compare("是") == 0;
        gs.keyName = mainSheetList.at(rowInt).at(2);
        gs.keyNames = split(gs.keyName, ",");
        gs.keys = getKeyValBatch(gs.keyNames);
        gs.key = gs.keys[0];
        gs.loop = string_To_int(mainSheetList.at(rowInt).at(3));
        // 如果是启用的
        if (gs.enable) {
            scriptList.push_back(gs);
            std::cout << gs << "\t";
            // 加载操作配置
            std::vector<std::vector<std::string>> operSheetList = readExcel(gs.name);
            std::vector<g_oper> operList = {};
            for (int rowInt = 1; rowInt < operSheetList.size(); rowInt++) {
                g_oper go;
                go.keyName = operSheetList.at(rowInt).at(0);
                go.down = string_To_int(operSheetList.at(rowInt).at(1));
                go.up = string_To_int(operSheetList.at(rowInt).at(2));
                go.dx = string_To_int(operSheetList.at(rowInt).at(3));
                go.dy = string_To_int(operSheetList.at(rowInt).at(4));
                go.keyNames = split(go.keyName, ",");
                go.keys = getKeyValBatch(go.keyNames);
                go.key = go.keys[0];
                operList.push_back(go);
            }
            operAllList.push_back(operList);
            finishMap.insert(std::make_pair(gs.idx, TRUE));
        }
    }
}

LPCWSTR stringToLPCWSTR(std::string orig) {
    size_t origsize = orig.length() + 1;
    const size_t newsize = 100;
    size_t convertedChars = 0;
    wchar_t* wcstring = (wchar_t*)malloc(sizeof(wchar_t) * (orig.length() - 1));
    mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);
    return wcstring;
}

bool checkWindow(HWND hwnd) {
    RECT rect;
    GetWindowRect(hwnd, &rect);
    int w = rect.right - rect.left, h = rect.bottom - rect.top;
    if (w <= 0 || h <= 0) {
        return false;
    }
    std::cout << endl << "提示：已捕获到窗口（宽=" << w << "，高=" << h << "）" << endl;
    return true;
}

// 获取窗口句柄
HWND getWindow(string className) {
    LPCWSTR classStr = stringToLPCWSTR(className);
    HWND hwnd = FindWindow(classStr, nullptr);
    if (!checkWindow(hwnd)) {
        hwnd = FindWindowEx(nullptr, nullptr, classStr, nullptr);
        if (!checkWindow(hwnd)) {
            std::cout << endl << "未捕获到窗口：" + className << endl;
            system("pause");
            exit(0);
        }
    }
    return hwnd;
}

// 读取窗口类名配置
string readClassName() {
    std::ifstream file("className.txt");
    if (file.is_open()) {
        std::string line;
        std::getline(file, line);
        file.close();
        return line;
    }
    else {
        return "UnrealWindow";
    }
}

// 初始化
void init() {
    // 脚本信息
    SetConsoleTitle(L"自动键鼠 v1.2.0");

    // 最小化窗口
    ShowWindow(GetConsoleWindow(), SW_MINIMIZE);

    // 读取读取窗口类名配置
    string className = readClassName();

    // 提示信息
    std::cout << "    自动键鼠 v1.2.0  --by gosick39（幻塔妙妙屋Q群：565943273）" << endl << endl;
    std::cout << "  注意：本程序仅学习使用，禁止商用！" << endl << endl;
    std::cout << "  使用说明：1.在“脚本方案.xlsx”中编辑你的方案（热键/按键支持配置0x开头的16进制键码）" << endl;
    std::cout << "            2.在“className.txt”中设置要自动点击的窗口类名（默认UnrealWindow）" << endl;
    std::cout << "            3.按下鼠标中键可读取窗口坐标" << endl;
    std::cout << "            4.暂停/继续键 F10" << endl;
    std::cout << "            5.停止键      F12" << endl;

    //  cout << endl;

     // 加载窗口句柄
    hwnd = getWindow(className);

    RECT rect;
    GetWindowRect(hwnd, &rect);
    int w = rect.right - rect.left, h = rect.bottom - rect.top;
    if (w <= 0 || h <= 0) {
        cout << endl << "警告：未捕获到窗口" << endl;
        //  system("pause");
         // exit(0);
    }
    HDC hdc = GetDC(hwnd);  // 获取窗口的设备上下文
    int dpi = GetDpiForWindow(hwnd);  // 获取窗口的 DPI 缩放比例
    ScaleViewportExtEx(hdc, dpi, dpi, dpi / 96, dpi / 96, nullptr);  // 缩放视窗区域
    ScaleWindowExtEx(hdc, dpi, dpi, dpi / 96, dpi / 96, nullptr);  // 缩放窗口区域
    SetForegroundWindow(hwnd);

    // 加载配置
    loadConfig();
}

// 执行方案
void handle(g_script gs, std::vector<g_oper> operList) {
    while (true) {
        for (int i = 0; i < operList.size(); i++) {
            g_oper go = operList.at(i);
            if (go.down >= 0) {
                if (go.key == 0x01) {
                    mouseDown(go.dx, go.dy);
                }
                else {
                    keyDown(go.keys);
                }
                Sleep(go.down);
            }
            if (go.up >= 0) {
                if (go.key == 0x01) {
                    mouseUp(go.dx, go.dy);
                }
                else {
                    keyUp(go.keys);
                }
                Sleep(go.up);
            }
        }
        gs.loop--;
        if (gs.loop <= 0) {
            finishMap[gs.idx] = TRUE;
            break;
        }
    }
}

// 输出窗口坐标
void printIdx() {
    // 获取鼠标当前位置的屏幕坐标
    POINT point;
    GetCursorPos(&point);
    // 将屏幕坐标转换为窗口坐标
    ScreenToClient(hwnd, &point);
    // 输出窗口坐标
    cout << endl << "窗口坐标：(" << point.x << ", " << point.y << ")";
}

// 热键处理
void hotKeyHandle() {
    while (TRUE) {
        // 按下鼠标中键，输出窗口坐标
        if (KEY_PRESSED(0x04)) {
            printIdx();
        }
        // 按下F10，暂停脚本
        if (KEY_PRESSED(VK_F10)) {
            pause = !pause;
        }
        // 按下F10，重启脚本
        if (KEY_PRESSED(VK_F12)) {
            // 获取当前控制台的窗口句柄
            HWND consoleWindow = GetConsoleWindow();
            // 启动一个新的控制台应用程序
            system("start AutoKM.exe");
            // 关闭当前控制台窗口
            PostMessage(consoleWindow, WM_CLOSE, 0, 0);
        }
        Sleep(200);
    }
}


void test() {
    std::vector<INT> keys = {};
    keys.push_back(VK_MENU);
    keys.push_back(0x31);

    int x = 1087;
    int y = 80;
    mouseClick(x, y, 100);
    Sleep(100);

    keyPress(keys, 100);
    Sleep(1000);
    keys.clear();
    keys.push_back(0x1B);
  
    keyPress(keys, 100);
}

int main() {
    // 初始化
    init();

    // 测试方法
    // test();

    // 启动热键处理线程
    thread hotKeyHandleTh(hotKeyHandle);
    hotKeyHandleTh.detach();

    while (true) {
        // 遍历脚本
        for (int i = 0; i < scriptList.size(); i++) {
            g_script gs = scriptList.at(i);
            std::vector<g_oper> operList = operAllList.at(i);
            // 如果是启用的
            if (gs.enable) {
                if (finishMap[gs.idx] && KEY_PRESSED(gs.key)) {
                    finishMap[gs.idx] = FALSE;
                    // 启动操作线程
                    thread th(handle, gs, operList);
                    th.detach();
                }
            }
        }

        Sleep(100);
    }
    return 0;
}